#include "cmsis_os.h"
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/sockets.h"

#include "FreeRTOS.h"
#include "task.h"
#include "tim.h"
#include "MBapp.h"
#include "common.h"
#include "ethernetif.h"
#include "backup.h"
#include "udp_comm.h"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>


#include "lwip/udp.h"
#include "lwip/timeouts.h"
#include "lwip/debug.h"
#include "fpga_nand.h"

// #define FPGA_LOOP_EN   1    // 屏蔽正常以太网通讯，否则一台设备自环测试

#define UDP_LOC_PORT    1003   // 局端UDP传输数据断开
#define UDP_REM_PORT    1003   // 远端UDP传输数据断开



#define UDP_HEADER_LEN  5   // 数据帧头：4个序号字节，1个数据类型字节
#define FPGA_DATA_LEN    1024   // 从FPGA读一帧数据长度
#define UDP_MAX_PAYLOAD_SIZE (UDP_HEADER_LEN + FPGA_DATA_LEN)

#define UDP_WRQ   2
#define UDP_DATA  3
#define UDP_ACK   4
#define UDP_ERROR 5


// enum udp_error {
//   UDP_ERROR_FILE_NOT_FOUND    = 1,
//   UDP_ERROR_ACCESS_VIOLATION  = 2,
//   UDP_ERROR_DISK_FULL         = 3,
//   UDP_ERROR_ILLEGAL_OPERATION = 4,
//   UDP_ERROR_UNKNOWN_TRFR_ID   = 5,
//   UDP_ERROR_FILE_EXISTS       = 6,
//   UDP_ERROR_NO_SUCH_USER      = 7
// };



struct udp_state {
    struct udp_pcb *upcb;
    ip_addr_t addr;    // 远端地址
    uint16_t port;           // 远端端口号

    uint16_t udp_sendlen;
    uint16_t udp_rcvlen;
    uint8_t udp_sendbuf[UDP_MAX_PAYLOAD_SIZE] ;
    uint8_t udp_rcvbuf[UDP_MAX_PAYLOAD_SIZE] ;

    uint32_t rcv_loss_Time; // 丢包时间
    uint32_t rcv_Time;      // 最后接收数据帧时间
    uint32_t stopTime;
    uint16_t RcvTickOut;   // 接收超时
    uint16_t SendTickOut;   // 发送超时
    uint16_t RcvFramCnt;   // 接收帧数量
};

static struct udp_state udp_state;
osMessageQId UDPQueueHandle;

// UDP数据发送
static uint8_t udp_demo_senddata(struct udp_state *udp_state, uint8_t soure)
{
	struct pbuf *ptr;
    static int framIndex = 0;
    udp_state->SendTickOut = 0;
    if(udp_state->stopTime > sys_now())
    {
        return 0;
    }
	ptr = pbuf_alloc(PBUF_TRANSPORT, udp_state->udp_sendlen, PBUF_POOL); //申请内存
	if(ptr)
	{
        udp_state->udp_sendbuf[0] = framIndex>>24;
        udp_state->udp_sendbuf[1] = framIndex>>16;
        udp_state->udp_sendbuf[2] = framIndex>>8;
        udp_state->udp_sendbuf[3] = framIndex;
        udp_state->udp_sendbuf[4] = soure;
        framIndex++;

		pbuf_take(ptr, udp_state->udp_sendbuf, udp_state->udp_sendlen); //将tcp_demo_sendbuf中的数据打包进pbuf结构中
		udp_send(udp_state->upcb, ptr);	//udp发送数据  建立连接使用
        // udp_sendto(udp_state->upcb, ptr, &udp_state.addr, udp_state.port);  // 不需要建立连接（UDP本来就不需要），需要指定地址与端口
		pbuf_free(ptr);//释放内存
	}
    return 0;
}


// 接收UDP数据回调
static void
udp_comm_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
    //struct pbuf *q;

	if(udp_state.port != port)
    {
        printf("udp_rcv ip:%0x port: %d\r\n",addr->addr, port);
        udp_state.port = port;
        udp_state.upcb->remote_port = port;

	}
    if (((udp_state.port != 0) && (port != udp_state.port)) ||
      (!ip_addr_isany_val(udp_state.addr) && !ip_addr_cmp(&udp_state.addr, addr)))
    {
        printf("recv port2 %d %d \r\n",port,udp_state.port);
        pbuf_free(p);
        return;
    }

    /*解析数据并处理 填充数据与发送*/
    if(p!=NULL)	//接收到不为空的数据时
    {
        // for(q=p;q!=NULL;q=q->next)  //遍历完整个pbuf链表
		// {
        //     if(q->len > UDP_MAX_PAYLOAD_SIZE)
        //     {
        //         printf("recv len too big %d \r\n",q->len);
        //     }
        //     else
        //     {
        //         // memset(udp_state.udp_rcvbuf, 0, sizeof(udp_state.udp_rcvbuf));
        //         udp_state.udp_rcvlen = q->len;
        //         memcpy(udp_state.udp_rcvbuf, q->payload, q->len);
        //         osMessagePut(UDPQueueHandle, UDP_ENV_RCV, 0U);
        //     }
        // }
        udp_state.udp_rcvlen = p->len;
        memcpy(udp_state.udp_rcvbuf, p->payload, p->len);
        osMessagePut(UDPQueueHandle, UDP_ENV_RCV, 0U);
        pbuf_free(p);
    }
    else
    {
        udp_disconnect(upcb);
        printf("udp_disconnect \r\n");
    }


}

err_t udp_comm_client_int(void)
{
    err_t ret;

    /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
    struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
    if (pcb == NULL) {
        return ERR_MEM;
    }

    //UDP客户端连接到指定IP地址和端口号的服务器
    ip_addr_t rmtipaddr;
    IP4_ADDR(&rmtipaddr, 192, 168, 1, 31);
    ret=udp_connect(pcb, &rmtipaddr, UDP_REM_PORT);

    ret = udp_bind(pcb, IP_ANY_TYPE, UDP_LOC_PORT);
    if (ret != ERR_OK) {
        udp_remove(pcb);
        return ret;
    }
    udp_state.port      = UDP_REM_PORT;
    udp_state.upcb      = pcb;
    memset(udp_state.udp_sendbuf, 0, sizeof(udp_state.udp_sendbuf));
    IP4_ADDR(&(udp_state.addr), 192, 168, 1, 31);
    udp_recv(pcb, udp_comm_recv, NULL);

    return ERR_OK;
}


/*
 * Initialize  server.
 */
err_t udp_comm_server_int(void)
{
    err_t ret;

    /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
    struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
    if (pcb == NULL) {
        return ERR_MEM;
    }


    ret = udp_bind(pcb, IP_ANY_TYPE, UDP_LOC_PORT);
    if (ret != ERR_OK) {
        udp_remove(pcb);
        return ret;
    }
    udp_state.port      = 0;
    udp_state.upcb      = pcb;
    memset(udp_state.udp_sendbuf, 0, sizeof(udp_state.udp_sendbuf));
    // 固定接收客服端设备UDP数据（客服端30，服务器31），后面需要动态变化
    IP4_ADDR(&(udp_state.addr), 192, 168, 1, 30);
    udp_recv(pcb, udp_comm_recv, NULL);

    return ERR_OK;
}

static uint32_t udpRunTick = 0;
uint8_t udpExtiType = 0, udpInterMs = 100;
static uint32_t sentIndex = 0, lossIndex = 0;
static uint32_t udpPara[4];
static uint32_t getFlag = 0;
uint32_t udp_getPara(uint8_t n)
{
    if(n >= sizeof(udpPara)/sizeof(uint32_t))
    {
        return 0;
    }
    return udpPara[n];
}

static void udp_comm_deal(void *thread_param)
{
	osEvent evt;
    #if(USE_UDP_TYPE == UDP_COMM_CLINET)
    udp_comm_client_int();
    #else
    udp_comm_server_int();
    #endif
    uint8_t samebuf, rcvType;
    uint32_t systick =0, tick1 = 0, tick2 = 0;
    uint32_t tick3 = 0, tick4 = 0;
    uint32_t sentlen = 0;
    uint32_t udpSpeed,rcvIndex = 0, lastRcvIndex = 0, lossCnt = 0;
    memset(udp_state.udp_sendbuf, 0xff, sizeof(udp_state.udp_sendbuf));
    fpga_One_write(0, 0x55); // 接收fifo启动
    #ifdef FPGA_LOOP_EN
    fpga_write(1, udp_state.udp_sendbuf, FPGA_DATA_LEN);
    #endif
    fpga_write(1, udp_state.udp_sendbuf, FPGA_DATA_LEN/4);
    while (1)
    {
        evt = osMessageGet(UDPQueueHandle, osWaitForever); // wait for message Forever
        systick = sys_now();
		if (evt.status == osEventMessage)
		{
			switch (evt.value.v)
			{
			case UDP_ENV_RCV:
                udp_state.RcvTickOut = 0;
                udp_state.RcvFramCnt ++;
                udp_state.rcv_Time = systick;

                rcvIndex = (udp_state.udp_rcvbuf[0] << 24) + (udp_state.udp_rcvbuf[1] << 16) \
                            + (udp_state.udp_rcvbuf[2] << 8) + udp_state.udp_rcvbuf[3];
                rcvType = udp_state.udp_rcvbuf[4];
                if(UDP_ENV_REMOTE_STOP == rcvType && UDP_HEADER_LEN == udp_state.udp_rcvlen)
                {
                    // 停止发送300ms
                    udp_state.stopTime = systick + 300;
                    printf("UDP rcv stop cmd\n");
                }
                else if(UDP_ENV_LIFE == rcvType && UDP_HEADER_LEN == udp_state.udp_rcvlen)
                {
                    printf("UDP rcv life cmd\n");
                }
                else if(UDP_TYPE_SAME == rcvType && (UDP_HEADER_LEN + 1) == udp_state.udp_rcvlen)
                {
                    samebuf = udp_state.udp_rcvbuf[UDP_HEADER_LEN];
                        // printf("UDP rcv same %d\n",samebuf);
                    memset(&udp_state.udp_sendbuf[UDP_HEADER_LEN], samebuf, FPGA_DATA_LEN);
                    fpga_write(1, &udp_state.udp_sendbuf[UDP_HEADER_LEN], FPGA_DATA_LEN);
                }
                else if(UDP_MAX_PAYLOAD_SIZE == udp_state.udp_rcvlen)
                {
                    memcpy(udp_state.udp_sendbuf, udp_state.udp_rcvbuf, UDP_MAX_PAYLOAD_SIZE);
                    fpga_write(1, &udp_state.udp_sendbuf[UDP_HEADER_LEN], FPGA_DATA_LEN);
                }

                if(0 == rcvIndex)
                {
                    lossCnt = 0;
                    // printf("UDP rcv start\n");
                }
                else
                if(rcvIndex != (lastRcvIndex + 1) || rcvIndex == lastRcvIndex)
                {
                    lossCnt++;
                    lossIndex = lastRcvIndex + 1;
                    udp_state.rcv_loss_Time = systick;
                }

                lastRcvIndex = rcvIndex;
                tick2 = systick;
                sentlen += udp_state.udp_rcvlen;
                if (tick2 - tick1 >= configTICK_RATE_HZ * 20)
                {
                    udpSpeed = ((uint64_t)sentlen*configTICK_RATE_HZ/125/(tick2 - tick1));
                    // printf("UDP rcv = %d kbps!, losCnt = %d rcvCnt = %d %d %d\n", udpSpeed,lossCnt, lastRcvIndex,lossIndex,sentlen);
                    udpPara[0] = udpSpeed;
                    udpPara[1] = lossCnt;
                    udpPara[2] = lastRcvIndex;
                    udpPara[3] = sentlen;
                    tick1 = tick2;
                    sentlen = 0;
                }
                // udp_state.udp_sendlen = UDP_HEADER_LEN;
                // udp_state.udp_sendbuf[udp_state.udp_sendlen++] = 'l';
                // udp_state.udp_sendbuf[udp_state.udp_sendlen++] = 'u';
                // udp_state.udp_sendbuf[udp_state.udp_sendlen++] = 'w';
                // udp_state.udp_sendbuf[udp_state.udp_sendlen++] = 'a';
                // udp_state.udp_sendbuf[udp_state.udp_sendlen++] = 'n';
                // udp_demo_senddata(&udp_state, UDP_ENV_RCV);
				break;
			// case UDP_ENV_EXTI: // 2  测试使用
            //     udp_state.udp_sendlen = UDP_HEADER_LEN;
            //     udp_state.udp_sendbuf[udp_state.udp_sendlen++] = 0xaa;
            //     udp_state.udp_sendbuf[udp_state.udp_sendlen++] = 0xaa;
            //     udp_state.udp_sendbuf[udp_state.udp_sendlen++] = 0xaa;
			// 	udp_demo_senddata(&udp_state, UDP_ENV_EXTI);
            //     if(UDP_ENV_EXTI == udpExtiType)
            //     {
            //         // printf("UDP send %d\n",sentIndex);
            //         osMessagePut(UDPQueueHandle, UDP_ENV_EXTI, 0U);
            //         if(udpInterMs)osDelay(udpInterMs);
            //     }
			// 	break;
            case UDP_ENV_DATA_SART:// 3  业务数据

                #ifdef FPGA_LOOP_EN
                static uint8_t readbuf;
                fpga_read(0, &udp_state.udp_sendbuf[UDP_HEADER_LEN], FPGA_DATA_LEN);
                for(int i =UDP_HEADER_LEN; i < UDP_HEADER_LEN+FPGA_DATA_LEN; i++)
                {
                    if(i == 0 && systick < 5000)
                    {

                    }
                    else
                    if(udp_state.udp_sendbuf[i] == (readbuf + 1))
                    {

                    }
                    else
                    if((0 == udp_state.udp_sendbuf[i]) && (0xff == readbuf))
                    {

                    }
                    else
                    {
                        // printf("fpga read err %d %d %d\n",i,readbuf, udp_state.udp_sendbuf[i]);
                    }
                    readbuf = udp_state.udp_sendbuf[i];
                }
                fpga_write(1, &udp_state.udp_sendbuf[UDP_HEADER_LEN], FPGA_DATA_LEN);
                #else
                fpga_read(0, &udp_state.udp_sendbuf[UDP_HEADER_LEN], FPGA_DATA_LEN);
                // if(udp_state.udp_sendbuf[UDP_HEADER_LEN] == Common_isAll(&udp_state.udp_sendbuf[UDP_HEADER_LEN], FPGA_DATA_LEN))
                // {
                //     udp_state.udp_sendlen = UDP_HEADER_LEN + 1;
				//     udp_demo_senddata(&udp_state, UDP_TYPE_SAME);
                // }
                // else
                {
                    udp_state.udp_sendlen = UDP_MAX_PAYLOAD_SIZE;
				    udp_demo_senddata(&udp_state, UDP_ENV_DATA_SART);
                }
                #endif
				break;
            case UDP_ENV_TEST_STOP:// 4
                lossCnt = 0;
                sentIndex = 0;
                printf("UDP_ENV_TEST_STOP \n");
                break;
            case UDP_ENV_REMOTE_STOP:// 5   向远端发送暂停操作（解决本地接收故障，远端一直发送，本地缓冲溢出无法恢复现象）
                //printf("UDP send stop %d\n",getFlag);
                udp_state.udp_sendlen = UDP_HEADER_LEN;
				udp_demo_senddata(&udp_state, UDP_ENV_REMOTE_STOP);
                break;
            case UDP_ENV_LIFE:// 7   心跳
                printf("UDP send life %d\n",getFlag);
                udp_state.udp_sendlen = UDP_HEADER_LEN;
				udp_demo_senddata(&udp_state, UDP_ENV_LIFE);
                break;

            case UDP_ENV_FIll_FF:// 6   填充
                // printf("UDP_ENV_FIll_FF %d\n",getFlag);
                memset(udp_state.udp_sendbuf, 0xff, FPGA_DATA_LEN/4);
                // fpga_write(1, udp_state.udp_sendbuf, FPGA_DATA_LEN/4);
                break;
			default:
				break;
			}
		}
    }
    osDelay(1);
 }

 void udp_comm_int(void)
 {
    osMessageQDef(udpQueue, 10, uint8_t);
	UDPQueueHandle = osMessageCreate(osMessageQ(udpQueue), NULL);
    HAL_TIM_Base_Start_IT(&htim1);
    sys_thread_new("udp_comm", udp_comm_deal, NULL, 512, osPriorityHigh);
 }


void udp_EXTI_IdleCallback(uint8_t envType, uint8_t interMs)
{
    udpExtiType = envType;
    udpInterMs = interMs;
    osMessagePut(UDPQueueHandle, envType, 0U);
}

// udp通讯状态，等显示：绿灯常亮：通讯正常  闪烁：通讯丢包 长灭：通讯断开
void udp_comm_Led(void)
{
    static uint32_t lastTime = 0;
    uint32_t nowTime = sys_now();
    if(nowTime > lastTime + 300)
    {
        // 2S没接收到数据，灯灭
        if(nowTime > udp_state.rcv_Time + 2000)
        {
            HAL_GPIO_WritePin(LED_LINK_GPIO_Port,LED_LINK_Pin, GPIO_PIN_SET);
        }
        else // 丢包后闪烁10S
        if(nowTime < udp_state.rcv_loss_Time + 10000 && udp_state.rcv_loss_Time > TICK_S(10))
        {
            HAL_GPIO_TogglePin(LED_LINK_GPIO_Port,LED_LINK_Pin);
        }
        else
        {
            HAL_GPIO_WritePin(LED_LINK_GPIO_Port,LED_LINK_Pin, GPIO_PIN_RESET);
        }
    }
}

static uint8_t timerCnt = 0;
void udp_Timer_100us(void)
{
    timerCnt++;
    udp_state.RcvTickOut++;
    udp_state.SendTickOut++;
    #ifndef FPGA_LOOP_EN
    // 1S没接收到远端数据（①没连接 ②本地接收缓存异常假死，让远端暂停发送）
    if(udp_state.RcvTickOut > 10000)
    {
        udp_state.RcvTickOut = 0;
        osMessagePut(UDPQueueHandle, UDP_ENV_REMOTE_STOP, 0U);
    }
    else // 0.5S没发送一帧数据，发送心跳（有业务不会出现这现象，测试时防止远端暂停发送使用）
    if(udp_state.SendTickOut > 5000)
    {
        udp_state.SendTickOut = 0;
        osMessagePut(UDPQueueHandle, UDP_ENV_LIFE, 0U);
    }
    // else if(UDP_ENV_DATA_SART == udpExtiType && (timerCnt >= udpInterMs))
    // {
    //     timerCnt = 0;
    //     osMessagePut(UDPQueueHandle, UDP_ENV_DATA_SART, 0U);
    // }
    #endif

}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	UNUSED(GPIO_Pin);
    getFlag++;
    // fifo收到足够数据，读出来传送
	if(GPIO_Pin == GPIO_PIN_7)
	{
		osMessagePut(UDPQueueHandle, UDP_ENV_DATA_SART, 0U);
	}
    // fifo内数据快没了，填充
	if(GPIO_Pin == GPIO_PIN_8)
	{
		osMessagePut(UDPQueueHandle, UDP_ENV_FIll_FF, 0U);
	}
}



